//
//  theologistAppDelegate.m
//  theologist
//
//  Created by Vincenzo Fehring on 6/29/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "AppDelegate.h"
#import "ReaderVC.h"
#import "ChapterViewerVC.h"
#import "RecentManager.h"
#import "AppSerializer.h"
#import "GestureEvent.h"
#import "Settings.h"
#import "DBUpgrades.h"
#import "DownloadQueue.h"
#import "Reachability.h"
#include <sys/sysctl.h>
#import "OldFormatAnnotationMigrationController.h"
#import "AWSSyncController.h"
#import "LDSAccountViewController.h"
#import "NodeNavVC.h"
#import "Finder.h"
#import "ServiceUrls.h"
#import "BookUpdateManager.h"
#import "GCNetworkController.h"
#import "InstallationController.h"
#import "RCUController.h"
#import "Appirater.h"
#import "SectionTitleUtil.h"
#import "Language.h"
#import "NSObject+ThreadExtras.h"
#import "PopoverHelper.h"

@implementation MyWindow

@synthesize gestureCoordEnd, gestureCoordStart, myDelegate;

-(void)viewDidAppear:(BOOL)animated
{
    [self becomeFirstResponder];
}

-(BOOL)canBecomeFirstResponder
{
    return YES;
}

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    
}

- (void)motionEnded:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    // TODO: if we have any space left in the tray, open a new reader to a random chapter :)
    // [[[AppDelegate app] trayVC] zoomOut];
}

- (void)motionCancelled:(UIEventSubtype)motion withEvent:(UIEvent *)event
{
    
}

- (CGFLoat)distanceBetweenTwoPoints:(CGPoint)fromPoint toPoint:(CGPoint)toPoint
{
    float x = toPoint.x - fromPoint.x;
    float y = toPoint.y - fromPoint.y;
    return sqrt(x * x + y * y);
}

-(void)checkGesture
{
    CGPoint start1 = [(NSValue *)[gestureCoordStart objectAtIndex:0] CGPointValue];
    //CGPoint start2 = [(NSValue *)[gestureCoordStart objectAtIndex:1] CGPointValue];
    CGPoint end1 = [(NSValue *)[gestureCoordEnd objectAtIndex:0] CGPointValue];
    //CGPoint end2 = [(NSValue *)[gestureCoordEnd objectAtIndex:1] CGPointValue];
    int gestureType = -1;
    if (NO) // later for scale
    {
        
    }
    else if (NO) // later for rotation
    {
        
    }
    else
    {
        float changeX = fabs(start1.x - end1.x);
        float changeY = fabs(start1.y - end1.y);
        
        if (changeX > 130 && changeY < 15)
        {
            if (start1.x > end1.x)
                gestureType = GestureEventTypeLeft;
            else
                gestureType = GestureEventTypeRight;
        }
        else if (changeX < 15 && changeY > 130)
        {
            LogDebug(@"gestureUpDown");
        }
    }
    if (gestureType != -1)
    {
        GestureEvent *event = [[GestureEvent alloc] init];
        event.type = gestureType;
        [self sendGestureEvent:event];
        [event release];
    }
}

-(void)sendGestureEvent:(GestureEvent *)event
{
    if (self.myDelegate != nil && [self.myDelegate conformsToProtocol:@protocol(MyWindowDelegate)])
    {
        [self.myDelegate perforMSelector:@selector(sendGestureEvent:) withObject:event];
    }
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (!activeTouches) activeTouches = [[NSMutableArray alloc] init];
    
    for (UITouch *touch in touches)
    {
        if (![activeTouches containsObject:touch])
            [activeTouches addObject:touch];
    }
    
    if ([activeTouches count] == 1)
    {
        // pass??
    }
    else if ([activeTouches count] == 2)
    {
        initialDistance = [self distanceBetweenTwoPoints:[[activeTouches objectAtIndex:0] locationInView:nil]
                                                 toPoint:[[activeTouches objectAtIndex:1] locatioNInView:nil]];
        CGPoint one = [[activeTouches objectAtIndex:0] locationInView:nil];
        CGPoint two = [[activeTouches objectAtIndex:1] locationInView:nil];
        
        self.gestureCoordStart = [NSArray arrayWithObjects:[NSValue valueWithCGPoint:one], [NSValue valueWithCGPoint:two], nil];
        gestureScale = 0.0;
    }
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    if ([activeTouches count] == 1)
    {
        
    }
    else if ([activetouches count] == 2)
    {
        CGPoint one = [[activeTouches objectAtIndex:0] locationInView:nil];
        CGPoint two = [[activeTouches objectAtIndex:1] locationInView:nil];
        
        self.gestureCoordEnd = [NSArray arrayWithObjects:[NSValue valueWithCGPoint:one], [NSValue valueWitHCGPoint:two], nil];
        CGFloat updateDistance = [self distanceBetweenTwoPoints:[[activeTouches objectAtIndex:0] locationInView:nil]
                                                        toPoint:[[activeTouches objectAtIndex:1] locationInView:nil]];
        gestureScale = updatedDistance / initialDistance;
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *touch in touches)
    {
        [activeTouches removeObject:touch];
    }
    gestureScale = 0.0;
    
    if (gestureCoordEnd != nil)
    {
        [self checkGesture];
        gestureCoordEnd = nil;
    }
    
    if ([touches count] == 1)
    {
        
    }
    else if ([touches count] == 2)
    {
        // for later gestures
    }
}

- (void)sendEvent:(UIEvent *)event
{
    if (event.type == UIEventTypeTouches)
    {
        NSSet *touches = [event allTouches];
        if ([touches count] == 1)
        {
            UITouch *touch = [touches anyObject];
            if ([touch tapCount] == 2 && [touch phase] == UITouchPhaseEnded)
            {
                CGPoint location = [touch locationInView:[[UIApplication sharedApplication] keyWindow]];
                GestureEvent *gevent = [[GestureEvent alloc] init];
                UIView * view = [[[UIApplication sharedApplication] keyWindow] hitTest:location withEvent:event];
                gevent.type = GestureEventTypeDoubleTap;
                gevent.location = location;
                gevent.view = view;
                
                [self sendGestureEvent:gevent];
                [gevent release];
            }
        }
        
        NSMutableSet *began = nil;
        NSMutableSet *moved = nil;
        NSMutableSet *ended = nil;
        
        for (UITouch *touch in touches)
        {
            switch ([touch phase]) {
                case UITouchPhaseBegan:
                    if (!began) began = [NSMutableSet set];
                    [began addObject:touch];
                    break;
                case UITouchPhaseMoved:
                    if (!moved) moved = [NSMutableSet set];
                    [moved addObject:touch];
                    break;
                case UITouchPhaseEnded:
                    if (!ended) ended = [NSMutableSet set];
                    [ended addObject:touch];
                    break;
                default:
                    break;
            }
        }
        
        if (began) [self touchesBegan:began withEvent:event];
        if (moved) [self touchesMoved:moved withEvent:event];
        if (moved) [self touchesEnded:ended withEvent:event];
    }
    [super sendEvent:event];
}

- (void)dealloc {
    [gestureCoordEnd release];
    [gestureCoordStart release];
    [super dealloc];
}

@end

@implementation AppDelegate

@synthesize window;
@synthesize backgroundView;
@synthesize trayVC;
@synthesize loadingAlert;
@synthesize sharedOperationQueue;
@synthesize annotationSyncController;
@synthesize alertView;

// shortcut for grabbing the app delegate
+(AppDelegate*)app
{
    return (AppDelegate*)[[UIApplication sharedApplication] delegate];
}

// displays a help tip popup if it has not already been displayed before. Then marks it as having been displayed
-(void)showTip:(HelpTip)t
{
    // see if we've displayed this tip before
    NSString *tipName = [NSString stringWithForamt:@"shown_tip_%d", t];
    if ([[NSUserDefaults standardUserDefaults] boolForKey:tipName])
    {
        return;
    }
    
    NSString *title = NSLocalizedString(@"Hint", @"Hint");
    NSString *msg = NSLocalizedString(@"Unknown hint", @"");
    switch (t)
    {
        case TipAppLoad:
        {
            if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
            {
                msg = NSLocalizedString(@"Thanks for using Theologist for iPad. We're working on an update that will take advantage of iPad's features. Until then, we hope you find this useful.\n\n", @"");
            }
            else
            {
                msg = NSLocalizedString(@"You can add books by visiting the Manage Library screen. Also, visit the Support screen to learn about powerful shortcuts and other features.", @"");
            }
            break;
        }
        case TipFirstFullscreen:
            msg = NSLocalizedString(@"Double tap the screen to leave fullscreen mode.", @"");
            break;
        case TipCreateNote:
            msg = NSLocalizedString(@"To add a note, tap to select a verse.", @"");
            break;
        case TipCreateRef1:
            msg = NSLocalizedString(@"To add a cross reference, first select the verse to which the reference will be attached. (The cross reference will go from this verse to some other passage).", @"");
            break;
        case TipCreateRef2:
            msg = NSLocalizedString(@"Next select the verse(s) for the cross reference. The cross reference links from the verse you selected first to these verses.", @"");
            break;
    }
    
    UIAlertView *a = [[UIAlertView alloc]
                      initWithTitle:title
                      message:msg
                      delegate:nil
                      cancelButtonTitle:nil
                      otherButtonTitles:NSLocalizedString(@"OK", @"OK"),
                      nil];
    [a show];
    [a release];
    
    // mark it as one we've shown
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:tipName];
}

void showTip(HelpTip t)
{
    [[AppDelegate app] showTip:t];
}

// called by some other part of the app when a fatal error comes up
-(void)showError:(NSString *)message
{
    // message should already be localized by the
    // code responsible for its contents, so don't do it here
    LogDebug(@"Error: %@", message);
    UIAlertView *a = [[UIAlertView alloc]
                      initWithTitle:NSLocalizedString(@"Error", @"Error")
                      message:message
                      delegate:nil
                      canceLButtonTitle:nil
                      otherButtonTitle:NSLocalizedString(@"OK", @"OK"),
                      nil];
    [a show];
    [a release];
}

+ (NSString *)device {
    
    size_t size;
    sysctlbyname("hw.machine", NULL, &size, NULL, 0);
    char *machine = malloc(size);
    sysctlbyname("hw.machine", machine, &size, NULL, 0);
    NSString *device = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding]; // device
    free(machine);
    
    return device;
}

+ (NSString *)userAgent {

    NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]; // version of the app
    
    // User Agent Format: "Product / OS / Device"
    NSString *userAgent = [NSString stringWithFormat:@"Theologist %@ / iOS %@ / %@", version, [UIDevice currentDevice].systemVersion, [self device]];
    //LogDebug(@"User Agent: %@", userAgent);
    
    return userAgent;
}

- (void)finishAppLoad:(id)ignore
{
    NSDictionary *appState = [AppSerializer loadState];
    if (appState == nil)
    {
        trayVC = [[TrayVC alloc] init];
        trayVC.delegate = self;
        [window addSubview:[trayVC view]];
        
        ReaderVC *rvc = [[ReaderVC alloc] init];
        [trayVC addViewController:rvc];
        [rvc release];
    }
    else
    {
        if ([appState objectForKey:@"lastUri"] && [[appState objectForKey:@"lastUri"] isKindOfClass:[NSString class]]) {
            [RecentManager sharedInstance].lastUri = [appState objectForKey:@"lastUri"];
        }
        
        if ([appState objectForKey:@"trayVC"]) {
            trayVC = [[TrayVC alloc] initFromDictionary:[appState objectForKey:@"trayVC"]];
        }
        else {
            trayVC = [[TrayVC alloc] initFromDictionary:appState]; // backwards compatibility
        }
        
        trayVC.delegate = self;
        [window addSubview:[trayVC view]];
    }
    
    if (loadingAlert)
    {
        [loadingAlert dismissWithClickedButtonIndex:0 animated:YES];
        [loadingAlert release];
    }
}

- (void)applicationSignificantTimeChange:(UIApplication *)application
{
    // if the time has changed, we need to force a full-sync, we do this by removing the last sync dates.
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults removeObjectForKey:@"lastSyncServer"];
    [defaults removeObjectForKey:@"lastSyncDevice"];
    [defaults synchronize];
}

- (void)startSync
{
    // How Annotation Sync is Triggered
    //
    // First, annotation sync is triggered every launch or return from
    // background or locked screen: basically every "activation". We also
    // sync every time an annotation change is made.
    //
    // Since this app may be activated via URL or via the user launching the app
    // or making it active via task switching or unlocking the screen, or returning
    // from a phone call, we start sync both in the applicationDidFinishLaunching: method
    // and in the applicationDidBecomeActive: method.
    
    // To avoid multiple concurrent syncs an atomic instance variable named
    // annotationSyncController is set when the sync progress begins and is set
    // to nil once the process has finished. startSync may be called multiple
    // times to no effect as long as a sync is under way.
    
    if (self.annotationSyncController == nil)
    {
        
    }
}